package com.sangeng.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sangeng.constants.SystemConstants;
import com.sangeng.domain.ResponseResult;
import com.sangeng.domain.dto.AddArticleDto;
import com.sangeng.domain.dto.UpdateArticleDto;
import com.sangeng.domain.entity.Article;
import com.sangeng.domain.entity.ArticleTag;
import com.sangeng.domain.entity.Category;
import com.sangeng.domain.vo.*;
import com.sangeng.enums.AppHttpCodeEnum;
import com.sangeng.mapper.ArticleMapper;
import com.sangeng.service.ArticleService;
import com.sangeng.service.ArticleTagService;
import com.sangeng.service.CategoryService;
import com.sangeng.utils.BeanCopyUtils;
import com.sangeng.utils.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private CategoryService categoryService;
    @Autowired
    private ArticleTagService articleTagService;
    /**
     * //查询热门前十的文章列表
     * 要求展示文章标题和浏览量。把能让用户自己点击跳转到具体的文章详情进行浏览(文章id)
     *注意：不能把草稿展示出来，不能把删除了的文章查询出来。要按照浏览量进行降序排序。
     * @return
     */
    @Override
    public ResponseResult hotArticleList() {
        //1.封装查询条件
        LambdaQueryWrapper<Article> lqw=new LambdaQueryWrapper<>();
        //1.1文章状态为已发布
        lqw.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
        //1.2要按照浏览量进行降序排序
        lqw.orderByDesc(Article::getViewCount);
        //2.封装分页条件
        //2.1只显示前十的文章
        Page<Article> page=new Page<>(1,10);
        //3.查询
        page(page,lqw);
        //4.结果统一封装成vo对象
        List<Article> records = page.getRecords();
//        List<HotArticleVo> hotArticleVoList=new ArrayList<>();
//        for (Article record : records) {
//            //4.1创建HotArticleVo对象
//            HotArticleVo hotArticleVo=new HotArticleVo();
//            //4.2属性拷贝
//            BeanUtils.copyProperties(record,hotArticleVo);
//            //4.3添加到集合中
//            hotArticleVoList.add(hotArticleVo);
//        }
        //4.结果统一封装成vo对象采用工具类来实现
        List<HotArticleVo> hotArticleVoList = BeanCopyUtils.copyBeanList(records, HotArticleVo.class);
        //5.返回结果
        return ResponseResult.okResult(hotArticleVoList);
    }
    /**
     * 分页查询文章列表
     * @param pageNum
     * @param pageSize
     * @param categoryId
     * @return
     */

    @Override
    public ResponseResult articleList(Integer pageNum, Integer pageSize, Long categoryId) {
        //1.查询所有的文章，正式发布（条件）
        LambdaQueryWrapper<Article> lqw=new LambdaQueryWrapper<>();
        lqw.eq(Article::getStatus,SystemConstants.ARTICLE_STATUS_NORMAL);
        //2.按照是否是置顶降序排序
        lqw.orderByDesc(Article::getIsTop);
        //3.加上分类条件(需要判断分类条件是否为空）
        lqw.eq(Objects.nonNull(categoryId)&&categoryId>0,Article::getCategoryId,categoryId);
        //4.加上分页条件
        Page<Article> page=new Page(pageNum,pageSize);
        //5.分页查询
        page(page,lqw);
        //6.查询每个文章的分类名称
        //6.1获取文章列表
        List<Article> records = page.getRecords();
        for (Article record : records) {
            //6.2获取分类id,在分类表中查询指定的分类
            Category category= categoryService.getById(record.getCategoryId());
            //6.3获取分类的名称,将值设置给文章类中的categoryName
            record.setCategoryName(category.getName());
            //6.4从redis查询每篇文章的最新浏览量
            Integer viewCount = redisCache.getCacheMapValue("article:viewCount", record.getId().toString());
            //6.5设置为最新的浏览量
            record.setViewCount(viewCount.longValue());
        }
        //7.统一结果封装
        List<ArticleListVo> articleListVos = BeanCopyUtils.copyBeanList(page.getRecords(), ArticleListVo.class);
        return ResponseResult.okResult(new PageVo(articleListVos,page.getTotal()));
    }
    /**
     * 获取文章详情
     * @param id
     * @return
     */

    @Override
    public ResponseResult getArticleDetail(Long id) {
        //1.根据id获取文章
        Article article = getById(id);
        //1.2从缓存中查询浏览量
        Integer viewCount = redisCache.getCacheMapValue("article:viewCount", id.toString());
        //1.3设置article的属性
        article.setViewCount(viewCount.longValue());
        //2.根据文章的分类id查询分类名称
        Category category = categoryService.getById(article.getCategoryId());
        String categoryName = category.getName();
        //3.属性拷贝并设置分类名称
        ArticleDetailVo articleDetailVo = BeanCopyUtils.copyBean(article, ArticleDetailVo.class);
        articleDetailVo.setCategoryName(categoryName);
        //4.返回结果
        return ResponseResult.okResult(articleDetailVo);
    }
    /**
     * 在查询详情文章时，更新文章的浏览量（redis中对应的数值自增）
     * @param id
     * @return
     */
    @Override
    public ResponseResult updateViewCount(Long id) {
        //往缓存中更新浏览量
        redisCache.incrementCacheMapValue("article:viewCount",id.toString(),1);
        return ResponseResult.okResult();
    }
    /**
     * 添加博文
     * @param addArticledto
     * @return
     */
    @Override
    //两个数据库操作要么同时成功要么同时失败，使用事务管理
    @Transactional
    public ResponseResult addArticle(AddArticleDto addArticledto) {
        //1.向文章表中插入数据
        //1.1属性拷贝
        Article article = BeanCopyUtils.copyBean(addArticledto, Article.class);
        //1.2添加数据
        boolean flag1= save(article);
        //2.向文章标签表中批量插入数据
        //2.1获取标签列表
        List<Long> tags = addArticledto.getTags();
        //2.2封装要插入文章标签中的数据
        List<ArticleTag> articleTagList = tags.stream()
                .map(tag -> new ArticleTag(article.getId(), tag))
                .collect(Collectors.toList());
        //2.3批量插入数据
        boolean flag2 = articleTagService.saveBatch(articleTagList);
        return flag1&&flag2?
                ResponseResult.okResult():ResponseResult.errorResult(AppHttpCodeEnum.ERR);
    }
    /**
     *在后台需要分页查询文章功能，要求能根据标题和摘要模糊查询。
     * @param pageNum
     * @param pageSize
     * @param title
     * @param summary
     * @return
     */

    @Override
    public ResponseResult<PageVo> selectAllByPage(int pageNum, int pageSize, String title, String summary) {
        //封装查询条件
        LambdaQueryWrapper<Article> lqw=new LambdaQueryWrapper<>();
        lqw.like(StringUtils.hasText(title),Article::getTitle,title);
        lqw.like(StringUtils.hasText(summary),Article::getSummary,summary);
        Page<Article> page=new Page<>(pageNum,pageSize);
        //分页查询
        page(page,lqw);
        return ResponseResult.okResult(new PageVo(page.getRecords(),page.getTotal()));
    }
    /**
     * 根据文章id查询数据，相应的文章所属标签也要查询
     * @param id
     * @return
     */
    @Override
    public ResponseResult<UpdateArticleVo> selectById(Long id) {
        //1.根据id查询文章详情
        Article article = getById(id);
        //2.根据文章id查询相应的文章标签表
        LambdaQueryWrapper<ArticleTag> lqw=new LambdaQueryWrapper<>();
        lqw.eq(ArticleTag::getArticleId,id);
        List<ArticleTag> articleTagList= articleTagService.list(lqw);
        //将标签列表进行数据转换
        List<Long> tags = articleTagList.stream().map(articleTag -> articleTag.getTagId())
                .collect(Collectors.toList());
        //将数据进行封装
        UpdateArticleVo updateArticleVo = BeanCopyUtils.copyBean(article, UpdateArticleVo.class);
        updateArticleVo.setTags(tags);
        return ResponseResult.okResult(updateArticleVo);
    }
    /**
     * 修改文章数据，同时要要进行文章标签表的更新
     * @param updateArticleDto
     * @return
     */

    @Override
    @Transactional
    public ResponseResult updateData(UpdateArticleDto updateArticleDto) {
        Article article = BeanCopyUtils.copyBean(updateArticleDto, Article.class);
        //进行文章表的更新
        updateById(article);
        List<Long> tags = updateArticleDto.getTags();
        //根据文章id进行文章标签删除
        LambdaQueryWrapper<ArticleTag> lqw=new LambdaQueryWrapper<>();
        lqw.eq(ArticleTag::getArticleId,article.getId());
        articleTagService.remove(lqw);
        //根据文章id进行文章标签插入
        List<ArticleTag> articleTagList = tags.stream().map(tag -> new ArticleTag(article.getId(), tag))
                .collect(Collectors.toList());
        articleTagService.saveBatch(articleTagList);
        return ResponseResult.okResult();
    }
    /**
     * 根绝文章id删除文章，同时也要删除文章标签表中的数据
     * @param id
     * @return
     */
    @Override
    @Transactional
    public ResponseResult deleteById(Long id) {
        removeById(id);
        LambdaQueryWrapper<ArticleTag> lqw=new LambdaQueryWrapper<>();
        lqw.eq(ArticleTag::getArticleId,id);
        articleTagService.remove(lqw);
        return ResponseResult.okResult();
    }


}